//============================================================================
// Name        : tree-match.cpp
// Author      : Long
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <boost/program_options.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "factor_graph.h"

namespace po = boost::program_options;
po::options_description desc("Allowed options");
po::variables_map vm;

int n, d;
std::string gin, fin, out;

void process_options(int argc, char** argv);
void usage(int exitcode = 0)
{
  std::cout << desc << std::endl;
  exit(exitcode);
}

int main(int argc, char** argv) {
  process_options(argc, argv);

  typedef  Factor<double> factor_type;
  typedef FactorGraph<factor_type> graph_type;
  if (vm.count("help"))
    usage();

  Universe u;
  u.addVariable(2).addVariable(3); // 2 variables;
  factor_type f;
  f.addArgument(0).addArgument(1); // function of 2 variables

  double d = 1;
  for (Assignment a = f.firstAssignment(); a.isValid(); a.nextAssignment(f.argument(), u))
    {
      f[a] = d++;
    }

  for (Assignment a = f.firstAssignment(); a.isValid(); a.nextAssignment(f.argument(), u))
    {
      std::cout << "f(" << a << ")=" << f[a] << std::endl;
    }

  u.setValue(0, 0);
  u.setValue(1, 2);
  std::cout << "f.value=" << f.value(u) << std::endl;

  Universe u1 = u;
  u1.setValue(1, -1); // free the first variable
  factor_type fr = f.reduce(u1);
  Argument fr_arg = fr.argument();
  std::cout << "fr.empty=" << (fr.empty()?"true":"false") << " fr.arg=" << fr_arg << std::endl;
  for (Assignment ar = fr.firstAssignment(); ar.isValid(); ar.nextAssignment(fr_arg, u1))
    {
      std::cout << "fr(" << ar << ")=" << fr[ar] << std::endl;
    }

  Universe u2 = u;
  u2.setValue(0, -1); // free the first variable
  factor_type f2 = f.reduce(u2);
  Argument f2_arg = f2.argument();
  std::cout << "f2.empty=" << (f2.empty()?"true":"false") << " f2.arg=" << f2_arg << std::endl;
  for (Assignment ar = f2.firstAssignment(); ar.isValid(); ar.nextAssignment(f2_arg, u2))
    {
      std::cout << "f2(" << ar << ")=" << f2[ar] << std::endl;
    }

  graph_type fg;
  fg.addFactor(f).addFactor(fr).addFactor(f2);
  std::cout << "product=" << fg.factorProduct(u) << std::endl;

  Universe uc = u.freeAll();
  //uc.setValue(1,2);
  Argument arg = fg.argument();
  std::cout << "fg.arg=" << arg << std::endl;

  graph_type::number_type Z = fg.normalizingConstant(uc);
  std::cout << "Z=" << Z << std::endl;

  Assignment a = fg.firstAssignment();
  a[0] = 1; a[1] = 1;
  std::cout << "probability(" << a << ")=" << fg.factorProduct(a)/Z << std::endl;

  Assignment map = fg.mapInference(uc);
  std::cout << "MAP (" << arg << ")=(" << map << ")" << std::endl;
  return 0;
}

void process_options(int argc, char** argv)
{
  desc.add_options()
    ("help", "produce this usage")
    ("gin", po::value<std::string>(&gin)->default_value(""), "input file (factor graph)")
    ("fin", po::value<std::string>(&fin)->default_value(""), "input file (factor functions)")
    ("out", po::value<std::string>(&out)->default_value(""), "output file")
    ;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);
}
